// 1.图片懒加载
let viewHeight = window.innerHeight || document.documentElement.clientHeight
let imgs = document.getElementsByTagName('img')
let n = 0 
function lazyLoad() {
	for(let i = n; i < imgs.length; i++) {
		let distance = imgs[i].getBoundingClientRect().top 
		if (viewHeight - distance >= 0) {
			imgs[i].src = imgs[i].getAttribute('data-src')
			n = i + 1
		}
	}
} 

// 2.节流
function throttle(fn, interval) {
	let last = 0 

	return function() {
		let context = this 
		let args = arguments 
		let now = Date.now() 

		if (now - last >= interval) {
			fn.apply(context, args)
			last = now
		}
	}
}

// 3.防抖
function debounce(fn, delay) {
	let timer = null 

	return function() {
		let context = this 
		let args = arguments 
		
		if (timer) {
			clearTimeout(timer)
		}
		timer = setTimeout(() => {
			fn.apply(context, args)
			clearTimeout(timer)
		}, delay)
	}
}

// 4.有底线的防抖
function throttleDebounce(fn, delay) {
	let last = 0, timer = null 

	return function() {
		let context = this 
		let args = arguments 

		let now = Date.now() 
		if (now - last >= delay) {
			fn.apply(context, args)
			last = now
		} else {
			clearTimeout(timer)
			timer = setTimeout(() => {
				fn.apply(context, args)
				last = now
			})
		}
	}
}

// 5.单例模式
class SingleDog {
	static getInstance() {
		if(!SingleDog.instance) {
			SingleDog.instance = new SingleDog()
		}

		return SingleDog.instance
	}
}

// 6.单例Modal
const Modal = (function() {
	let modal = null 

	return function() {
		if (!modal) {
			modal = document.createElement('div')
			modal.id = 'modal'
			modal.innerHTML = '我是一个弹窗'
			modal.style.display = 'none'
			document.body.appendChild(modal)
		}
		return modal
	}
})()

// 7.深拷贝
function deepClone(obj) {
	if (typeof obj !== 'object' || obj === null) {
		return obj
	}
	let copy = {}
	if (Array.isArray(obj)) {
		copy = []
	}

	for(let key in obj) {
		if (obj.hasOwnProperty(key)) {
			copy[key] = deepClone(obj[key])
		}
	}

	return copy
}

// 8.数组对象去重
function unique(arr) {
	let obj = []
	return arr.filter(item => {
		return !obj[item.id] && (item.id = true)
	})
}

// 9.数组对象去重
function unique(arr) {
	return arr.reduce((pre, cur) => {
		if (!pre.find(item => item.id === cur.id)) {
			pre.push(cur)
		}
		return pre
	}, [])
}

// 10.千分位 正则
function thousand(num) {
	num = num.toString()
	let reg = /(-?\d+)(\d{3})/
	while(reg.test(num)) {
		num = num.replace(reg, '$1,$2')
	}
	return num
}
// 11.千分位 for 循环
function thousand(num) {
	num = num.toString() 
	let result = '', n = 0 

	for(let i = num.length - 1; i >= 0; i--) {
		n++
		result = num[i] + result 
		if (i !== 0 && n %3 === 0) {
			result = ',' + result
		}
	}
	return result
}

// 12.手写new 
function myNew(constructor, ...args) {
	let obj = {}
	obj.__proto__ = constructor.prototype 
	let result = constructor.apply(obj, args)

	return typeof result === 'object' ? result : obj
}

// 13.手写 apply 
Function.prototype.apply = function(thisObj, args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	if (!thisObj) {
		thisObj = window
	} else {
		thisObj = Object(thisObj)
	}
	let fn = Symbol() 
	thisObj[fn] = this 
	let result = thisObj[fn](...args)
	delete thisObj[fn]
	
	return result
}

// 14.手写 call 
Function.prototype.call = function(thisObj, ...args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	if (!thisObj) {
		thisObj = window
	} else {
		thisObj = Object(thisObj)
	}
	let fn = Symbol() 
	thisObj[fn] = this 
	let result = thisObj[fn](...args)
	delete thisObj[fn]

	return result
}

// 15.手写 bind 
Function.prototype.bind = function(thisObj, ...args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	if (!thisObj) {
		thisObj = window
	} else {
		thisObj = Object(thisObj)
	}
	let _this = this 
	let newBind = function(...secondArgs) {
		let isNew = this instanceof newBind 
		let context = isNew ? this : thisObj 

		return _this.call(context, ...args, ...secondArgs)
	}

	if (_this.prototype) {
		newBind.prototype = Object.create(_this.prototype)
	}

	return newBind
}

// 16.手写 EventEmit 
class EventEmit {
	constructor() {
		this.handlers = {}
	}
	on(eventName, callback) {
		if (!this.handlers[eventName]) {
			this.handlers[eventName] = []
		}
		this.handlers[eventName].push(callback)
	}
	emit(eventName, ...args) {
		if(this.handlers[eventName] ) {
			this.handlers[eventName].forEach(cb => {
				cb(...args)
			})
		}
	}
	off(eventName, callback) {
		if (this.handlers[eventName]) {
			let index = this.handlers[eventName].indexOf(callback)
			if (index !== -1) {
				this.handlers[eventName].splice(index, 1)
			}
		}
	}
	once(eventName, callback) {
		const wrapper = (...args) => {
			callback(...args)
			this.off(eventName, wrapper)
		} 
		this.on(eventName, wrapper)
	}
}

// 手写sort
Array.prototype.sort = function(callback) {
	if (typeof callback !== 'function') {
		callback = function(a, b) {
			return a.localeCompare(b)
		}
	}
	let tempArr = this
	for (let i = 0; i < tempArr.length - 1; i++) {
		for(let j = 0; j < tempArr.length - 1 - i; j++) {
			if (callback(tempArr[j], tempArr[j + 1]) > 0) {
				[tempArr[j], tempArr[j + 1]] = [tempArr[j + 1], tempArr[j]]
			}
		} 
	}

	return tempArr
}
// 17.手写filter 
Array.prototype.filter = function(callback, thisObj) {
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	if (typeof callback !== 'function') {

	}
	let tempArr = this, result = []
	for(let i = 0; i < tempArr.length; i++) {
		if (callback.call(thisObj, tempArr[i], i, tempArr)) {
			result.push(tempArr[i])
		}
	}
	return result
}

// 18.手写map 
Array.prototype.map = function(callback, thisObj) {
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	if (typeof callback !== 'function') {
		return new TypeError('not a function')
	}
	let tempArr = this, result = []
	for(let i = 0; i < tempArr.length; i++) {
		result[i] = callback.call(thisObj, tempArr[i], i, tempArr)
	}
	return result
}

// 19.手写forEach 
Array.prototype.forEach = function(callback, thisObj) {
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	if (typeof callback !== 'function') {
		return new TypeError('not a function')
	}
	let tempArr = this
	for(let i = 0; i < tempArr.length; i++) {
		callback.call(thisObj, tempArr[i], i, tempArr)
	}
}

// 20.手写reduce 
Array.prototype.reduce = function(callback, initValue) {
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	if (typeof callback !== 'function') {
		return new TypeError('not a function')
	}
	let tempArr = this, account = initValue, k = 0 
	
	if (!account) {
		account = tempArr[k++]
	}
	while(k < tempArr.length) {
		account = callback.call(null, account, tempArr[k], k, tempArr)
		k++
	}

	return account
	
}

// 21.函数柯里化


// 22.数组扁平化

// 23.数组扁平化 


// 24.手写 instanceof 


// 25.手写Object.is 

// 26.手写Object.assign 


// 27.Promise 异步加载图片


// 28.手动封装一个JSONP


// 29.Promise 对象 实现Ajax 


// 30.滚动加载 


// 31.将 VirtualDom 转化为真实的DOM结构
/**
 * tag
 * attrs
 * children
 */
// Virtual DOM => DOM 


// 32.递归阶乘


// 33.阶乘尾递归优化


// 34.斐波那契数列


// 35.斐波那契数列 循环代替递归


// 36.斐波那契数列 记忆化优化


// 37.模拟实现Promise
// Promise利用三大手段解决回调地狱：
// 1. 回调函数延迟绑定
// 2. 返回值穿透
// 3. 错误冒泡


// 38.实现有并行限制的Promise调度器


// 39.冒泡排序


// 数组反转


// 40.模拟打印机打字效果


// 41.让 for of 可以遍历对象

// 42.无重复字符的最长子串
// 给定一个字符串，请你找出其中不含有重复字符的 最长子串 的长度。

